﻿using System;
using System.Collections.Generic;
using EnCor.Security;
using EnCor.Config;
using EnCor.ServiceLocator;
using Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration;
using EnCor.CallingTrack;
using EnCor.Logging;
using EnCor.Hosting;
using System.Threading;

namespace EnCor
{
    public class EnCorEnviroment
    {
        private readonly Dictionary<string, Module> _Modules = new Dictionary<string,Module>();

        private readonly EnCorConfig _Config;

        private IServiceContainer _Container;

        private SecurityCenter _SecurityCenter;

        private IList<FacadeHost> _Hosts;

        private string _ExecutePath;

        private ILog _Logger;

        internal EnCorEnviroment(EnCorConfig config)
        {
            _Config = config;
        }

        private void InitContainer()
        {
            _Container = new UnityServiceContainer();
        }

        public T GetService<T>()
        {
            return _Container.GetService<T>();
        }

        private void LoadModules(ModuleConfigCollection modules)
        {
            foreach (ModuleConfig moduleConfig in modules)
            {
                var module = (Module)Activator.CreateInstance(moduleConfig.Type);
                foreach ( ServiceConfig serviceConfig in moduleConfig.Services)
                {
                    serviceConfig.VerifyConfig();
                }
                ModuleConfigContext context = new ModuleConfigContext(_Container, moduleConfig);
                context.AddPolicy(BuildServiceTrackerPolicy());
                module.DoInit(context);
                _Modules.Add(moduleConfig.Name, module);
            }
        }

        private Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyData BuildServiceTrackerPolicy()
        {
            PolicyData policy = new PolicyData();
            policy.Name = "serviceTracker";
            MatchingRuleData matchingRule = new CustomMatchingRuleData("BizServicePublicMatchingRole", typeof(BizServicePublicMethodMatchingRole));
            policy.MatchingRules.Add(matchingRule);
            CallHandlerData callHandlerData = new CustomCallHandlerData("BizServiceCallTrack", typeof(ServiceCallHandler));
            policy.Handlers.Add(callHandlerData);
            return policy;
        }

        internal void InitializeSecurityContext()
        {
            _SecurityCenter.InitializeSecurityContext();
        }

        internal SecurityCenter SecurityCenter
        {
            get
            {
                return _SecurityCenter;
            }
        }

        private void StartModules()
        {
            foreach ( Module module in _Modules.Values)
            {
                module.DoStart();
            }
        }

        internal void StartUp()
        {
            // initialize logging module
            LoggingInitializer loggingInitializer = new LoggingInitializer();
            loggingInitializer.Initlize(_Config.Logging);

            // initialize internal container
            InitContainer();

            // initialize security
            _SecurityCenter = new SecurityCenter();
            _SecurityCenter.Config(_Config.Security);

            // load modules
            LoadModules(_Config.Modules);

            HostFactory factory = new HostFactory();
            _Hosts = new List<FacadeHost>();
            foreach (FacadeHostConfiguration hostConfig in _Config.Hosting)
            {
                FacadeHost host = factory.Create(null, hostConfig);
                _Hosts.Add(host);
            }

            InitExecutePath();

            _Logger = LogManager.GetLogger("EnCor");

            _Logger.Info("Environment initialized successful");


            StartModules();

            _Logger.Info("Modules started");

            try
            {
                foreach (FacadeHost host in _Hosts)
                {
                    host.Start();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("EnCorEnviroment.StartUp Exception:" + ex.Message);
                /*
                foreach (FacadeHost host in _Hosts)
                {
                    host.Stop();
                }
                throw;
                 */
            }

            _Logger.Info("Hosts started");
        }

        internal void Stop()
        {
            foreach ( Module module in _Modules.Values)
            {
                module.DoStop();
            }
            _Logger.Info("Modules started");

            foreach (FacadeHost host in _Hosts)
            {
                host.Stop();
            }
            _Logger.Info("Hosts stoped");
        }

        private void InitExecutePath()
        {
            _ExecutePath = AppDomain.CurrentDomain.BaseDirectory;
        }
    }
}
